package com.rraj.common.redis.dataRedis;

import com.rraj.common.utils.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis 操作类
 * User: hqm
 * Date: 2017/6/14
 *
 */
@Component
public class RedisClient {


    private static final Logger LOGGER = LoggerFactory.getLogger(RedisClient.class);


    @Autowired
    private RedisTemplate redisTemplate;


    /**

    private SetOperations<String, String> setOperations;//=redisTemplate.opsForSet();


    private static final Logger LOGGER = LoggerFactory.getLogger("distributedlock");


    /*************************************key 操作******************************************/


    /**
     * @param keyFormat
     * @param keyValues
     */
    public void del(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.delete(key);
    }

    /**
     * 设置key的过期时间
     *
     * @param keyFormat
     * @param keyValues
     */
    public void expire(String keyFormat, Integer seconds, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * 在指定点时间点过期
     *
     * @param keyFormat
     * @param date
     * @param keyValues
     */
    public void expireAt(String keyFormat, Date date, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.expireAt(key, date);
    }


    /*************************************String 操作******************************************/

    /**
     * 指定key设置值
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     *
     * @param keyFormat
     * @param keyValues
     */
    public void set(String keyFormat, Object value, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(key, value);
    }


    /**
     * 指定点key设置值,并且设置过期时间
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     *
     * @param keyFormat     KEY的格式如:KEY{ID}
     * @param keyValues     如：1234，最后生成：KEY1234
     * @param expireSeconds 失效时间
     * @param value         实际的值
     */
    public void set(String keyFormat, Object value, long expireSeconds, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
    }


    /**
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     *
     * @param keyFormat
     * @param keyValues
     * @param <T>
     * @return
     */
    public <T> T get(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Object o = redisTemplate.opsForValue().get(key);
        if (o != null) {
            return (T) o;
        }
        return null;
    }

    /**
     *计数器
     */

    /**
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     * 专用于计数器数值的获取
     *
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public Long getNumber(String keyFormat, String... keyValues) {

        String key = format(keyFormat, keyValues);
        String result = (String) redisTemplate.opsForValue().get(key);
        if (result != null) {
            return Long.valueOf(result);
        }
        return 0L;
    }


    /**
     * 计数器的初始化
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     *
     * @param keyFormat
     * @param keyValues
     */
    public void setNumber(String keyFormat, Long value, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(key, String.valueOf(value));
    }

    /**
     * 计数器的初始化
     * keyFormat KEY的格式如:KEY_{ID}_{USERID} 如果keyValues是 1234，5432 最后生成的cache key是：KEY_1234_5432
     *
     * @param keyFormat     KEY的格式如:KEY{ID}
     * @param keyValues     如：1234，最后生成：KEY1234
     * @param expireSeconds 失效时间
     * @param value         实际的值
     */
    public void setNumber(String keyFormat, Long value, long expireSeconds, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
    }


    /**
     * 计数器，自增计数
     *
     * @param keyFormat
     * @param value
     * @param keyValues
     */
    public Long incrBy(String keyFormat, Long value, String... keyValues) {
        String key = format(keyFormat, keyValues);
        return redisTemplate.opsForValue().increment(key, value);
    }


    /**********************************set操作*******************************************/
    /**
     * 获取set中所有的集合
     *
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public Set<String> sMembers(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Set<String> values = redisTemplate.opsForSet().members(key);
        return values;
    }

    /**
     * 查询key的数量
     *
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public Long scard(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Long count = redisTemplate.opsForSet().size(key);
        if (count == null) {
            return 0l;
        }
        return count;
    }


    /**
     * 求所有所有key的并集
     *
     * @param keys
     * @return
     */
    public Set<String> sUnion(Set<String> keys) {
        String key = "";
        Set<String> values = redisTemplate.opsForSet().union(key, keys);
        return values;
    }

    /**
     * 求所有keys的并集，结果存入指定的key
     *
     * @param keys
     * @param resultKey
     * @return
     */
    public Long sUnionAndStore(Set<String> keys, String resultKey) {
        if (CollectionUtils.isEmpty(keys)) {
            return -1l;
        }

        String key = keys.iterator().next();
        keys.remove(key);

        Long result = redisTemplate.opsForSet().unionAndStore(key, keys, resultKey);
        return result;
    }


    /**
     * key1与key2的差集
     *
     * @param key1
     * @param key2
     * @return
     */
    public Set<String> sDiff(String key1, String key2) {
        Set<String> values = redisTemplate.opsForSet().difference(key1, key2);
        return values;
    }

    /**
     * 向集合添加元素
     *
     * @param key1
     * @param value
     * @param keyValues
     * @return
     */
    public Long sAdd(String key1, String value, String... keyValues) {
        String key = format(key1, keyValues);
        Long result = redisTemplate.opsForSet().add(key, value);
        return result;
    }

    /**
     * 移除单个元素
     *
     * @param key1
     * @param value
     * @param keyValues
     * @return
     */
    public Long sRem(String key1, String value, String... keyValues) {
        String key = format(key1, keyValues);
        return redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * 移除并返回集合中的一个随机元素
     *
     * @param key1
     * @param keyValues
     * @return
     */
//    public String sPop(String key1, String... keyValues) {
//        String key = format(key1, keyValues);
//        String result = setOperations.pop(key);
//        return result;
//    }


    /**********************************zset操作*******************************************/


    /**
     * *******************************List操作******************************************
     */


    /*********************************hash操作******************************************/


    /**
     * 将HashMap中的Key对应的Value加上一个值
     *
     * @param keyFormat
     * @param field
     * @param value
     * @param keyValues
     */
    public void hIncrBy(String keyFormat, String field, long value,
                        String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForHash().increment(key, field, value);
    }

    /**
     * 删除HashMap field字段
     *
     * @param keyFormat
     * @param field
     * @param keyValues
     */
    public void hDel(String keyFormat, String field, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForHash().delete(key, field);
    }


    /**
     * 获取HashMap 所有值
     *
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public Map<String, String> hGetAll(String keyFormat, String... keyValues) {
        String key = format(keyFormat, keyValues);
        Map<String, String> result = redisTemplate.opsForHash().entries(key);
        return result;
    }

    /**
     * 获取hash中指定key 的值
     *
     * @param keyFormat
     * @param hashKey
     * @param keyValues
     * @return
     */
//    public String hGet(String keyFormat, String hashKey, String... keyValues) {
//        String key = format(keyFormat, keyValues);
//        String result = hashOperations.get(key, hashKey);
//        return result;
//    }

    /**
     * @param keyFormat
     * @param hashKey
     * @param keyValues
     * @return
     */
    public void hPut(String keyFormat, String hashKey, String value, String... keyValues) {
        String key = format(keyFormat, keyValues);
        redisTemplate.opsForHash().put(key, hashKey, value);
    }


    /**
     * 获取分布式锁
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public boolean getDistributedLock(String keyFormat,String... keyValues){

        String key = format(keyFormat, keyValues);

        try {
            String value=String.valueOf(System.currentTimeMillis());

            while (true){
                Boolean getLock=redisTemplate.opsForValue().setIfAbsent(key, value);
                if(getLock){
                    //获取锁成功
                    LOGGER.debug(Thread.currentThread().getName() + " get distributed lock success a");
                    return true;
                }else {
                    //获取锁失败
                    LOGGER.debug(Thread.currentThread().getName() + " get distributed lock fail b");

                    while (true){
                        Object t1Object=redisTemplate.opsForValue().get(key);
                        long t1,t2=0;
                        if(t1Object!=null){
                            t1=Long.parseLong(t1Object.toString());
                            if(System.currentTimeMillis()-t1>3000){
                                //锁过期
                                LOGGER.debug(Thread.currentThread().getName() + " get distributed lock expire c");

                                Object t2Object= redisTemplate.opsForValue().getAndSet(key, System.currentTimeMillis());
                                if(t2Object!=null){
                                    t2=Long.parseLong(t2Object.toString());
                                    if(t1==t2){
                                        //获得锁
                                        LOGGER.debug(Thread.currentThread().getName() + " get distributed lock success d");

                                        return true;
                                    }else {
                                        //在这之前已经有其他客户端获取锁，等待重试
                                        LOGGER.debug(Thread.currentThread().getName() + " get distributed lock retry e");

                                        try {
                                            Thread.sleep(50);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }else {
                                    //锁被释放，可以获得锁
                                    LOGGER.debug(Thread.currentThread().getName() + " get distributed lock retry f");

                                    break;
                                }
                            }else {
                                //锁正在有效期被其他客户端使用，等待重试
                                LOGGER.debug(Thread.currentThread().getName() + " get distributed lock retry h");

                                try {
                                    Thread.sleep(50);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }else {
                            //锁被释放，可以进行获取锁操作
                            LOGGER.debug(Thread.currentThread().getName()+" get distributed lock retry i");

                            break;
                        }
                    }
                }
            }
        }catch (Exception ex){
            LOGGER.error(ex.getMessage(),ex);
            return true;
        }
    }

    public boolean tryGetDistributedLock(String keyFormat,String... keyValues){
        String key = format(keyFormat, keyValues);
        String value=String.valueOf(System.currentTimeMillis());

        Boolean getLock=redisTemplate.opsForValue().setIfAbsent(key, value);
        if(getLock){
            //获取锁成功
            return true;
        }
        return false;
    }

    /**
     * 释放分布式锁
     * @param keyFormat
     * @param keyValues
     * @return
     */
    public boolean releaseDistributedLock(String keyFormat,String... keyValues){
        String key = format(keyFormat, keyValues);
        redisTemplate.delete(key);

        LOGGER.info(Thread.currentThread().getName() + " release distributed lock");

        return true;
    }




    /**
     * 格式化Key
     */
    public static String format(String formatKey, String... keyValues) {
        if (keyValues == null || keyValues.length == 0) {
            return formatKey;
        }
        StringBuilder key = new StringBuilder();
        char[] chars = formatKey.toCharArray();
        int index = -1;
        boolean inmark = false;
        boolean firstinmark = false;
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];
            if (ch == '{') {
                index++;
                inmark = true;
                firstinmark = true;
            } else if (ch == '}') {
                inmark = false;
            } else if (inmark) {
                if (firstinmark) {
                    firstinmark = false;
                    key.append(keyValues[index]);
                }
            } else {
                key.append(chars[i]);
            }
        }
        return key.toString();
    }

    public static String stepFormat(String formatKey, String... keyValues) {
        if (keyValues == null || keyValues.length == 0) {
            return formatKey;
        }
        StringBuilder key = new StringBuilder();
        char[] chars = formatKey.toCharArray();
        int index = -1;
        boolean inmark = false;
        boolean firstinmark = false;
        for (int i = 0; i < chars.length; i++) {
            char ch = chars[i];
            if (ch == '{') {
                index++;
                inmark = true;
                firstinmark = true;
            } else if (ch == '}') {
                inmark = false;
            } else if (inmark) {
                if (firstinmark) {
                    firstinmark = false;
                    if(index<keyValues.length){
                        key.append(keyValues[index]);
                    }else {
                        key.append(chars,i-1,chars.length-i+1);
                    }
                }
            } else {
                key.append(chars[i]);
            }
        }
        return key.toString();
    }

}
